//
// MacroscopicModelConfigurator.java
//
// @author Michael Liljenstam, ISTS, Dartmouth College
//
// $Id: MacroscopicModelConfigurator.java,v 1.3 2003/03/08 02:00:49 mili Exp $

package SSF.App.Worm;

import SSF.OS.*;
import SSF.Net.*;
import SSF.Net.Util.*;
import com.renesys.raceway.DML.*;

//import SSF.OS.BGP4.Util.*;

// ==== SSF.App.Worm.MacroscopicModelConfigurator class ==================/
/**
 * Common code for configuring options to the macroscopic model.
 *
 * @author Michael Liljenstam, ISTS, Dartmouth College
 */
public abstract class MacroscopicModelConfigurator extends ProtocolSession {

  /************************** Global model ******************************/

  /** Reference to the global epidemic model. */
  static MacroscopicModel        wormModel;

  /** ProtocolSession that created this object. Used as time reference. */
  static ProtocolSession         triggerSession;
 
  /** Epidemic model implementation class name. */
  static String epidemicClass = "SSF.App.Worm.DeterministicWormEpidemic";

  /** Name of initializer implementation class for epidemic model. */
  static String initializerClass = 
      "SSF.App.Worm.UniformWormEpidemicInitializer";

  /** Name of class for implementation of removal rate function. */
  static String gammaClass = "SSF.App.Worm.ZeroGammaFunction";

  /** Traffic model implementation class name if used, otherwise null. */
  static String trafficClass = null;

  /** Switch to select if a homogeneous epidemic model should be used 
   *  (default), or a model that is stratified by AS. */
  static boolean              stratifiedOn = false;

  /** Name of AS topology file, if stratified model is used. */
  static String               asGraphFileName = null;

  /** Seed string for infection Random Number stream. */
  static String               infectionRNGseed = null;

  /** Time step. */
  float                       deltaT = 60.0F; // 1 min

  // ....... start values for epidemic model........
  /** Initial S, i.e. initial number of susceptible hosts. */
  static int                  s_0; // required param
  /** Initial I, i.e. initial number of infected hosts. */
  static int                  i_0 = 1; // default = 1 infected host
  /** Beta, the infection parameter. Controls infection rate. */
  static float                beta = 0; // required param

  /** Single worm mean scan rate. */
  static float                meanScanRate = 4.76F; // 4.76 probes/s (CR)

  /** Print out debug information for macroscopic model. */
  static boolean              wormModelDebug = false;


  // .......... Helper classes ........................................../

  /**
   * Timer that drives the global epidemic model.
   */
  public class MacroModelTimer extends Timer {
    MacroscopicModelConfigurator owner = null;
      
    public MacroModelTimer(ProtocolGraph e, long dt, 
				    MacroscopicModelConfigurator _owner) {
      super(e, dt);
      owner = _owner;
    }

    public void callback() {
      owner.update();
      set();
    }
  }


  /************************** Methods    ********************************/

  /** 
   *  Initalize (after config phase). First instance creates global epidemic 
   *  model object and initiates the timer that drives the epidemic model. 
   *  All instances register with the epidemic model so that it can find them 
   *  to trigger infection events.
   */
  public void init_macroscopic() {
    // create worm model
    if (wormModel == null) {
      // create worm model
      wormModel = new MacroscopicModel(asGraphFileName, infectionRNGseed, 
				       wormModelDebug, 
				       epidemicClass, initializerClass, 
				       gammaClass, trafficClass, 
				       s_0, i_0, beta,
				       meanScanRate);

      // setup "driver" timer
      (new MacroModelTimer(inGraph(), SSF.Net.Net.seconds(deltaT), 
			   this))
	  .set(SSF.Net.Net.seconds(deltaT));
    }
  }


  /**
   *  Configure options for the macroscopic model, epidemic model etc.
   * 
   *  Whenever the <tt>WormProtocolSession</tt> or 
   *  <tt>GatewayProtocolSession</tt> is used, the 
   *  outermost <b>Net</b> <i>must have</i> a global definition of the 
   *  <b>worm_model [ ... ]</b> attribute. The <b>worm_model</b> attributes 
   *  are as follows:
   *
   *  <p>
   *  REQUIRED ATTRIBUTES: <br>
   *  <ul>
   *    <li><b>Epidemic</b>      --- settings for the worm epidemic.
   *                                 Further described below.</li>
   *  </ul><p>
   *
   *  OPTIONAL ATTRIBUTES:<br> 
   *  <ul>
   *    <li><b>Traffic</b>          --- Invoke worm induced traffic model and 
   *                                    supply settings.
   *                                    Further described below.</li>
   *
   *    <li><b>delta_t</b> %F       --- set the time step size (in seconds).
   *                                    </li>
   *
   *    <li><b>stratified_on</b> %S --- stratified model (true or false).</li>
   *
   *    <li><b>as_graph</b> %S      --- name of AS graph file (connectivity).
   *                                    Required for stratified model.</li>
   *
   *    <li><b>debug</b> %S         --- "debug true" will switch on debugging 
   *                                    printouts for the global epidemic 
   *                                    process.</li>
   *
   *  </ul><p>
   *  
   *  The <b>Epidemic</b> attribute has the following attributes:
   * 
   *  <p>
   *  REQUIRED ATTRIBUTES: <br>
   *  <ul>
   *    <li><b><i>s_0</i></b> %I  -- inital number of suscepible hosts in 
   *                                  the whole of the Internet.</li>
   *
   *    <li><b><i>i_0</i></b> %I  -- inital number of infected hosts</li>
   *
   *    <li><b><i>beta</i></b> %F -- infection parameter.</li>
   *
   *  </ul><p>
   *
   *  OPTIONAL ATTRIBUTES:<br> 
   *  <ul>
   *    <li><b>use</b> %S         -- specify epidemic model implementation 
   *                                 class to use. Default is 
   *                                 <tt>DeterministicWormEpidemic</tt>
   *                                 </li>
   *
   *    <li><b>initializer</b> %S -- specify initializer implementation class 
   *                                 to use. Default is 
   *                                 <tt>UniformWormEpidemicInitializer</tt>.
   *                                 </li>
   *
   *    <li><b>gamma_function</b> %S -- specify removal function 
   *                                 implementation class to use. Default is 
   *                                 <tt>ZeroGammaFunction</tt> (no removals).
   *                                 </li>
   *  </ul><p>
   *
   *
   *  The <b>Traffic</b> attribute has the following attributes:
   * 
   *  <p>
   *  REQUIRED ATTRIBUTES: <br>
   *  <ul>
   *  </ul><p>
   *
   *  OPTIONAL ATTRIBUTES:<br> 
   *  <ul>
   *    <li><b>use</b> %S         -- specify traffic model implementation 
   *                                 class to use. Default is 
   *                                 <tt>MeanRateWormTraffic</tt>
   *                                 </li>
   *  </ul><p>
   */
  public void configGlobalOptions(Configuration cfg, Configuration topNetCfg) {
    // Infection RNG seed, create by using global seed and append this name
    try {
      Configuration rngCfg = 
	  (Configuration)topNetCfg.findSingle("randomstream");
      if (rngCfg != null) {
	  String globStreamName = (String)cfg.findSingle("stream");
	  if (globStreamName != null) {
	      infectionRNGseed = globStreamName + "/MacroscopicModel";
	  }
      }
    } catch (configException e) {
	System.err.println("worm_model: " + e);
    }

    // time step
    try {
      if (cfg.findSingle("delta_t") != null) {
        deltaT = Float.parseFloat((String)cfg.findSingle("delta_t"));
	System.err.println("worm_model deltaT: " + deltaT);
      } 
    } catch (configException e) {
	System.err.println("worm_model: " + e);
    }

    // select model implementations
    try {
      Configuration epidemicCfg;
      if ((epidemicCfg = (Configuration)cfg.findSingle("Epidemic")) != null) {
	configEpidemic(epidemicCfg);
      } else {
	System.err.println("ERROR in worm_model config: Must configure epidemic");
	System.exit(-1);
      }
    } catch (configException e) {
	System.err.println("worm_model: " + e);
    }
    try {
      Configuration trafficCfg;
      if ((trafficCfg = (Configuration)cfg.findSingle("Traffic")) != null) {
	configTraffic(trafficCfg);
      } else {
	  // don't use traffic model
      }
    } catch (configException e) {
	System.err.println("worm_model: " + e);
    }
    

    // look for debug flag
    try {
      if((cfg.findSingle("debug") != null) && 
	 (((String)cfg.findSingle("debug")).compareTo("true") == 0))
	  wormModelDebug =  true;
    } catch (configException e) {
	System.err.println("worm_model: " + e);
    }

    // stratify model by AS? default is homogeneous (non-stratified)
    try {
      if((cfg.findSingle("stratified_on") != null) && 
	 (((String)cfg.findSingle("stratified_on")).compareTo("true") == 0))
	  stratifiedOn =  true;
    } catch (configException e) {
	System.err.println("worm_model: " + e);
    }

    // set AS graph file (required if stratified model)
    try {
      asGraphFileName = (String) cfg.findSingle("as_graph");
      if(stratifiedOn && asGraphFileName == null) {
	  System.err.println("Config error in worm_model " 
			     + ": 'as_graph' is a required attribute for stratified model");
	  System.exit(-1);
      }
    } catch (configException e) {
	System.err.println("worm_model: " + e);
    }
  }

  /**
   *  Configure 'Epidemic' keyword for worm epidemic model settings.
   *
   *  See config() method for details.
   */
  public void configEpidemic(Configuration cfg) {

    // 'use': implementation
    try {
      String useStr = (String) cfg.findSingle("use");
      if(useStr != null) {
	  epidemicClass = useStr;
      } else {
	  // use default
      }
    } catch (configException e) {
	System.err.println("worm_model: " + e);
    }
    // initializer class
    try {
      String initStr = (String) cfg.findSingle("initializer");
      if(initStr != null) {
	  initializerClass = initStr;
      } else {
	  // use default
      }
    } catch (configException e) {
	System.err.println("worm_model: " + e);
    }
    // gamma function class
    try {
      String gammaStr = (String) cfg.findSingle("gamma_function");
      if(gammaStr != null) {
	  gammaClass = gammaStr;
      } else {
	  // use default
      }
    } catch (configException e) {
	System.err.println("worm_model: " + e);
    }

    // init epidemic model parameters
    try {
      String s0_str = (String) cfg.findSingle("s_0");
      if(s0_str != null) {
	  s_0 = Integer.parseInt(s0_str);
      } else {
	  System.err.println("Config error in worm_model.Epidemic: " 
			     + "'s_0' is a required attribute");
	  System.exit(-1);
      }
    } catch (configException e) {
	System.err.println("worm_model.Epidemic: " + e);
    }
    try {
      String i0_str = (String) cfg.findSingle("i_0");
      if(i0_str != null) {
	  i_0 = Integer.parseInt(i0_str);
      }
    } catch (configException e) {
	System.err.println("worm_model.Epidemic: " + e);
    }
    try {
      String beta_str = (String) cfg.findSingle("beta");
      if(beta_str != null) {
	  beta = Float.parseFloat(beta_str);
      } else {
	  System.err.println("Config error in worm_model.Epidemic: " 
			     + "'beta' is a required attribute");
	  System.exit(-1);
      }
    } catch (configException e) {
	System.err.println("worm_model.Epidemic: " + e);
    }
  }

  /**
   *  Configure 'Traffic' attribute for worm induced scan traffic model 
   *  settings.
   */
  private void configTraffic(Configuration cfg) {
    // 'use': implementation
    try {
      String useStr = (String) cfg.findSingle("use");
      if(useStr != null) {
	  trafficClass = useStr;
      }
    } catch (configException e) {
	System.err.println("worm_model.Traffic: " + e);
    }
  }

  /**
   * getSimTime: get the simulation time
   * it's the same function used in tcpSessionMaster.java
   */
  public float getSimTime() {
    return inGraph().now()/(float)SSF.Net.Net.seconds(1);
  }

  /**
   * Do one timestep update of the worm model.
   * 
   */
  public void update() {
    float t = getSimTime(); // time in secs
    float newWormModelTime = wormModel.update(t);
    if (wormModelDebug)
      System.err.println("t: " + t
			 + " Macroscopic model update, new t=" 
			 + newWormModelTime
			 + " s, i=" + wormModel.getTotalInfections());
  }

}
